﻿using MyKindom.GameError;
using MyKindom.BuildingSystem;
using Godot;
using System;
using System.IO;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.ComponentModel;
using System.Reflection;
using System.Linq;
using System.Xml.Linq;

namespace ZLTools
{
    public class JsonDataReader
    {
        public static JsonDataReader Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new JsonDataReader();
                }
                return instance;
            }
        }

        private static JsonDataReader instance;

        private string buildingDataPath;
        private JObject buildingDataJsonObject;

        public Dictionary<string, JObject> DataMap;

        private JsonDataReader()
        {
            DataMap = new Dictionary<string, JObject>();
            SetBuildingDataToMap();
        }

        private void SetBuildingDataToMap()
        {
            buildingDataPath = "res://config_files/BuildingData.json";
            buildingDataJsonObject = new JObject();
            int ret = ParseJsonFileTojsonDataObject(buildingDataPath, ref buildingDataJsonObject);

            DataMap["BuildingData"] = buildingDataJsonObject;
        }

        private int ParseJsonFileTojsonDataObject(string jsonFilePath, ref JObject jsonDataObject)
        {
            using var file = Godot.FileAccess.Open(jsonFilePath, Godot.FileAccess.ModeFlags.Read);
            string jsonDataString = file.GetAsText();
            jsonDataObject = JObject.Parse(jsonDataString);

            if (jsonDataObject != null && jsonDataObject.Count > 0)
            {
                return (int)GameErrorCode.OPERATE_SUCCESS;
            }
            GD.Print("Error: Failed to parse json file to json data object");
            return (int)GameErrorCode.OPERATE_FAILED;
        }

        public int GetBuildingData(string buildingName, ref BuildingData buildingData)
        {
            string currentBuildingDataString = DataMap["BuildingData"][buildingName].ToString();
            JObject currentBuildingJObject = JObject.Parse(currentBuildingDataString);

            // ================================================================================
            buildingData.BuildingName = currentBuildingJObject["BuildingName"].ToString();
            buildingData.BuildingType = currentBuildingJObject["BuildingType"].ToString();
            buildingData.PhysicLayer = currentBuildingJObject["PhysicLayer"].ToString();
            buildingData.NavigationType = currentBuildingJObject["NavigationType"].ToString();

            buildingData.TileSetIndex = (int)currentBuildingJObject["TileSetIndex"];
            buildingData.TileMapPosition = GetVector2FromJson((JArray)currentBuildingJObject["TileMapPosition"]);

            //JArray buildingComponentsArray = (JArray)currentBuildingJObject["BuildingComponents"];
            //buildingData.BuildingComponents = ConvertJArrayToList(buildingComponentsArray);
            // ================================================================================

            if (buildingData.BuildingName != null)
            {
                return (int)GameErrorCode.OPERATE_SUCCESS;
            }
            GD.Print("Error: Failed to get building data");
            return (int)GameErrorCode.OPERATE_FAILED;
        }

        private Vector2I GetVector2FromJson(JArray jsonArray)
        {
            Vector2I vectorFromJson = new Vector2I((int)jsonArray[0], (int)jsonArray[1]);
            return vectorFromJson;
        }

        public List<string> ConvertJArrayToList(JArray jArray)
        {
            List<string> list = new List<string>();

            foreach (JToken item in jArray)
            {
                if (item.Type is JTokenType.String)
                {
                    list.Add(item.ToString());
                }
            }

            return list;
        }

        public int GetAllMainObjectInJObject(string jsonDataName, List<JObject> jsonMainObjectList)
        {
            JObject jsonData = DataMap[jsonDataName];

            foreach (JProperty property in jsonData.Properties())
            {
                if (property != null && property.Value is JObject)
                {
                    JObject jsonObject = property.ToObject<JObject>();
                    jsonMainObjectList.Add(jsonObject);
                }
            }

            if (jsonMainObjectList.Count > 0)
            {
                return (int)GameErrorCode.OPERATE_SUCCESS;
            }
            GD.PrintErr("Error: Failed to get the main json object list");
            return (int)GameErrorCode.OPERATE_FAILED;
        }

        public int GetJsonString(string jsonFilePath, string targetJsonName, ref string getTargetJsonString)
        {
            using var file = Godot.FileAccess.Open(jsonFilePath, Godot.FileAccess.ModeFlags.Read);
            string jsonDataString = file.GetAsText();
            JObject jsonDataObject = JObject.Parse(jsonDataString);

            if (jsonDataObject == null)
            {
                GD.PrintErr("Error: The json data object is empty");
                return (int)GameErrorCode.OPERATE_SUCCESS;
            }

            getTargetJsonString = jsonDataObject[targetJsonName].ToString();
            if (!string.IsNullOrEmpty(getTargetJsonString))
            {
                return (int)GameErrorCode.OPERATE_SUCCESS;
            }
            GD.PrintErr("Error: Failed to get json string.");
            return (int)GameErrorCode.OPERATE_FAILED;
        }

        public void ClassTest()
        {
            GD.Print("Test success");
        }

        // Parse json Object to struct type
        public int ConvertJsonToStruct<Tstruct>(JObject jsonObject, Tstruct structObject) where Tstruct : struct
        {
            var structProperties = typeof(Tstruct).GetProperties(BindingFlags.Public | BindingFlags.Instance);

            foreach (var jsonProperty in jsonObject.Properties())
            {
                var structProperty = structProperties.FirstOrDefault(p => p.Name == jsonProperty.Name);
                if (structProperty == null)
                {
                    return (int)GameErrorCode.OPERATE_SUCCESS;
                }

            }

            GD.PrintErr("Error: Failed to convert json to struct");
            return (int)GameErrorCode.OPERATE_FAILED;
        }
    }
}
